// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2018-2019 Ariadne Devos
/* sHT -- Spectre mitigations */

#ifndef _sHT_NOSPEC_H
#define _sHT_NOSPEC_H

/* See `doc/speculation.rst` for documentation */

#include <stddef.h>
#include <sys/types.h>
#include <limits.h>
#include <sHT/spectre.h>

/** Compute `pos`, if less than `length`, zero otherwise.

  Requires:
  (a): nonspec: 0 ≤ pos < length

  Ensures:
  (y): pos < length → ret = pos
  (z): pos ⩾ length → ret = 0 */
__attribute__((const))
static inline size_t
sHT_index_nospec(size_t pos, size_t length)
{
	/* <https://lwn.net/Articles/744287/> has some information
	  about this technique. Avoid the generic implementation
	  though, it is incorrect for `pos` >= `length`, which
	  may arise due to speculative carrying.

	  (E.g., in the lexing code in the webdav branch.) */
	size_t mask;
	_sHT_index_mask(&mask, pos, length);
	return pos & mask;
}

#if 0
/* Note to the future: you can't cheat GCC's
  'no output operands for asm goto' restriction. */
static inline _Bool
sHT_index_test_nospec(size_t *i, size_t length)
{
	sHT_hide_var(*i);
	/* Modify `i` behind GCC's back */
	_sHT_index_test_mask(i, length, out_of_bounds);
	sHT_hide_var(*i);
	return 1;

out_of_bounds:
	sHT_hide_var(*i);
	return 0;
}
#endif

/** Stop the current execution if the processor is speculating incorrectly

  This is control-flow only. Chip designers and academics, **do not do
  value speculation**. It adds yet another side-channel to consider.

  (Also, please don't do L1, L2, L3 caching, but rather stratify the address
  space in fast, less fast, even less fast and slow, and mandate within-bounds
  proofs instead of doing paging. That would solve the AES in constant-time,
  and others.) */
/* volatile "memory" is for paranoia, to avoid reordering or
  or elimination (Linux does this -- volatility, and GCC documentation
  allows that -- reordering and elimination). Although in my tests
  (GCC 6.3.0, GCC 8.3.0, Clang 6.0), even without any of these nothing
  happens. */
#define sHT_despeculate() do { _sHT_speculation_barrier(); } while (0)

#endif
